#include "config.h"
#include "system.h"
#include "ansidecl.h"
#include "coretypes.h"
#include "tm.h"
#include "opts.h"
#include "tree.h"
#include "tree-iterator.h"
#include "tree-pass.h"
#include "gimple.h"
#include "toplev.h"
#include "debug.h"
#include "options.h"
#include "flags.h"
#include "convert.h"
#include "diagnostic-core.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "target.h"
#include "cgraph.h"
#include "vec.h"

#include "gmp.h"
#include "mpfr.h"

#include "js-lang.h"
#include "js-tree.h"
#include "js-utils.h"
#include "js-builtin.h"
#include "js-symbol.c"

#define int3() asm volatile("int3");

VEC(tree,gc) *global_decls;
VEC(js_symbol,gc) *global_declsym;
VEC(js_symbol,gc) *global_undef_declsym;
#define IS_FUNCTION_DECL(decl) ((decl) && TREE_CODE(decl) == FUNCTION_DECL)
#define IS_STMT_LIST(stmt) (stmt && TREE_CODE(stmt) == STATEMENT_LIST)
#define JS_TREE_TYPE(node) ((TREE_TYPE(node))? TREE_TYPE(node):integer_type_node)

#define IS_char_array(n) (TREE_TYPE(n) && TREE_TYPE(TREE_TYPE(n)) == char_type_node)
static tree js_get_function_tree(const char *name, VEC(tree,gc) *args);
static void js_build_function_set_decl_stmt(tree fn_decl, VEC(tree,gc) *decls, tree stmts);

static tree js_make_block(void)
{
  tree block = make_node(BLOCK);
  TREE_TYPE(block) = void_type_node;
  TREE_USED(block) = true;
  return block;
}

static tree js_build_empty_stmt(void)
{
  return build_empty_stmt(0/*input_location*/);
}

tree js_build_id(const char *str)
{
  char *name = xstrdup(str);
  int i, len = strlen(name);
  for (i = 0; i < len; i++) {
      if (name[i] == '$') {
          name[i] = '_';
      }
  }
  return get_identifier(name);
}

tree js_build_int(jsint_t val)
{
  return build_int_cst(integer_type_node, val);
}

tree js_build_float_str(const char *buf)
{
  REAL_VALUE_TYPE d;
  real_from_string(&d, buf);
  return build_real(double_type_node, d);
}

tree js_build_string(const char *str)
{
  int len = strlen(str);
  tree t = build_string(len, str);
  tree idxtype = build_index_type(build_int_cst(NULL_TREE, len));
  tree type = build_array_type(char_type_node, idxtype);
  TREE_TYPE(t) = type;
  TREE_CONSTANT(t) = true;
  TREE_READONLY(t) = true;
  TREE_STATIC(t) = true;
  return t;
}

tree js_build_this_expr(void)
{
  return this_node;
}

tree js_build_int_object(tree x)
{
    VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
    VEC_safe_push(tree, gc, args, save_expr(x));
    return js_build_call_expr(new_int_node, args, false/*hasSelf*/, false);
}
tree js_build_float_object(tree x)
{
    VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
    VEC_safe_push(tree, gc, args, save_expr(x));
    return js_build_call_expr(new_float_node, args, false/*hasSelf*/, false);
}


static bool is_field_accessor_expr(tree expr, bool isGetter)
{
  if (TREE_CODE(expr) == CALL_EXPR) {
      tree fn = CALL_EXPR_FN(expr);
      tree id  = DECL_NAME(TREE_OPERAND(fn, 0));
      if (isGetter) {
          if (id == getter_node)
              return true;
          else if (id == get_prototype_node) {
          }
      } else {
          if (id == setter_node)
              return true;
          else if (id == set_prototype_node)
              return true;
      }
  }
  return false;
}
#define IS_GETTER(expr) (is_field_accessor_expr(expr, true))
#define IS_SETTER(expr) (is_field_accessor_expr(expr, false))

#define BLOCK_EXPR_BODY(NODE) BLOCK_SUBBLOCKS(NODE)

static int max_block_level = -1;
static tree *block_level_stmts = NULL;
static VEC(tree,gc) **block_level_decls = NULL;
static VEC(js_symbol,gc) **block_level_args = NULL;

#define JS_STMTLIST_INIT ((tree)0xdeadbeaf)
#define JS_DECLLIST_INIT ((VEC(tree,gc*))0xdeadbeaf)
#define JS_ARGSLIST_INIT ((VEC(js_symbol,gc*))0xdeadbeaf)

static void expand_current_datas(int cur, int level)
{
    int i, size = level + 1;
    tree *stmts = block_level_stmts;
    VEC(tree,gc) **decls = block_level_decls;
    VEC(js_symbol,gc) **args  = block_level_args;
    stmts = (tree *) xrealloc(stmts, size * sizeof(tree));
    decls = (VEC(tree,gc) **) xrealloc(decls, size * sizeof(VEC(tree,gc)*));
    args  = (VEC(js_symbol,gc) **)
      xrealloc(args, size * sizeof(VEC(js_symbol,gc)*));
    max_block_level = size;
    block_level_stmts = stmts;
    block_level_decls = decls;
    block_level_args = args;
    for (i = cur; i < size; i++) {
        block_level_stmts[i] = JS_STMTLIST_INIT;
        block_level_decls[i] = JS_DECLLIST_INIT;
        block_level_args[i]  = JS_ARGSLIST_INIT;
    }
}

#define DEF_CHECK_INIT(T, mode, defval, init) \
static T check_init_##mode (int level) {\
  if (max_block_level <= level) {\
      expand_current_datas(level, level+1);\
  }\
  if (block_level_##mode[level] == defval) {\
      block_level_##mode[level] = init;\
  }\
  return block_level_##mode[level];\
}
DEF_CHECK_INIT(tree, stmts, JS_STMTLIST_INIT, alloc_stmt_list())
DEF_CHECK_INIT(VEC(tree,gc)*, decls, JS_DECLLIST_INIT, VEC_alloc(tree,gc,0))
DEF_CHECK_INIT(VEC(js_symbol,gc)*, args, JS_ARGSLIST_INIT, VEC_alloc(js_symbol, gc, 0))

#define CHECK_INIT(name, level) check_init_##name (level)

void js_append_to_current_stmtlist(tree stmt, int level)
{
  tree current_stmts;
  current_stmts = CHECK_INIT(stmts, level);
  append_to_statement_list(stmt, &current_stmts);
  block_level_stmts[level] = current_stmts;
}

void js_append_to_current_decls(tree decl, int level)
{
  VEC(tree,gc) *current_decls;
  current_decls = CHECK_INIT(decls, level);
  VEC_safe_push(tree,gc, current_decls, decl);
  block_level_decls[level] = current_decls;
}

void js_append_to_current_args(tree decl, int level)
{
  VEC(js_symbol,gc) *current_args;
  current_args = CHECK_INIT(args, level);
  /* append "this" variable to args vector. */
  if (!current_args) {
      tree type = ptr_type_node;
      js_symbol_t *sym = XNEW(js_symbol_t);
      sym->name = this_node;
      sym->decl = build_decl(CURRENT_LOCATION(), PARM_DECL, NULL_TREE, object_type_ptr);
      DECL_ARG_TYPE(sym->decl) = type;
      VEC_safe_push(js_symbol,gc, current_args, sym);
      block_level_args[level] = current_args;
  }
  if (decl != NULL_TREE) {
      tree type = object_type_ptr;
      js_symbol_t *sym = XNEW(js_symbol_t);
      sym->name = decl;
      sym->decl = build_decl(CURRENT_LOCATION(), PARM_DECL, decl, type);
      DECL_ARG_TYPE(sym->decl) = type;
      VEC_safe_push(js_symbol,gc, current_args, sym);
      block_level_args[level] = current_args;
  }
}

#define DEF_GET_LEVEL(T, name) \
T get_level_##name (int level) \
{\
  return CHECK_INIT(name, level);\
}

tree get_level_stmts(int level)
{
  return CHECK_INIT(stmts, level);
}

VEC(tree,gc) *get_level_decls(int level)
{
  return CHECK_INIT(decls, level);
}

VEC(js_symbol,gc) *get_level_args(int level)
{
  return CHECK_INIT(args, level);
}

#define DEFCLEAR(mode, fname) \
void fname (int level) {\
  block_level_##mode[level] = NULL;\
}

DEFCLEAR(stmts, clear_level_stmts)
DEFCLEAR(decls, clear_level_decls)
DEFCLEAR(args , clear_level_args)
#define clear_level(level) \
  clear_level_stmts(level);\
  clear_level_decls(level);\
  clear_level_args(level)

static int check_level_with_expr(tree expr)
{
  int i;
  for (i = 0; i < max_block_level; i++) {
      tree stmts = get_level_stmts(i);
      if (TREE_CODE(expr) == STATEMENT_LIST && stmts) {
          tree_stmt_iterator tsi1, tsi2;
          tsi2 = tsi_start(stmts);
          for (tsi1 = tsi_start(expr); !tsi_end_p(tsi1); tsi_next(&tsi1)) {
              tree t1, t2;
              if (tsi_end_p(tsi2))
                  goto L_next;
              t1 = tsi_stmt(tsi1);
              t2 = tsi_stmt(tsi2);
              if (t1 != t2)
              tsi_next(&tsi2);
          }
          return i;
      }
L_next:;
      if (stmts) {
          tree_stmt_iterator tsi;
          for (tsi = tsi_start(stmts); !tsi_end_p(tsi) ; tsi_next(&tsi)) {
              tree t = tsi_stmt(tsi);
              if (t == expr) {
                  return i;
              }
          }
      }
  }
  return 0;
}

static tree js_tree_chain(VEC(tree, gc) *vec, tree t)
{
  int i;
  tree x = NULL_TREE;
  if (VEC_length(tree, vec) > 0) {
      FOR_EACH_VEC_ELT(tree, vec, i, x) {
#if 0
          if (TREE_CODE(x) == VAR_DECL) {
              tree nm = DECL_NAME(x);
              const char *s = IDENTIFIER_POINTER(nm);
              fprintf(stderr, "decl:%s\n", s);
          }
#endif

          TREE_CHAIN(x) = t;
          t = x;
      }
  }
  return t;
}

typedef struct treeitr {
    tree (*init) (tree x);
    tree (*apply) (tree x, tree arg);
    tree (*exit) (tree x);
} treeitr_t;


static tree treeitr_build_init(tree x ATTRIBUTE_UNUSED)
{
  return alloc_stmt_list();
}

static tree treeitr_build_apply(tree x, tree arg)
{
  tree stmts = arg;
  append_to_statement_list(x, &stmts);
  return stmts;
}

static tree treeitr_build_exit(tree x)
{
  return x;
}

static treeitr_t treeitr_build_stmtlists = {
    treeitr_build_init,
    treeitr_build_apply,
    treeitr_build_exit,
};

static tree treeitr_dump_init(tree x)
{
  return x;
}

static tree treeitr_dump_apply(tree x, tree arg)
{
  fprintf(stderr, "%s\n", tree_code_name[TREE_CODE(x)]);
  return arg;
}

static tree treeitr_dump_exit(tree x)
{
  return x;
}

static treeitr_t treeitr_dump_tree = {
    treeitr_dump_init,
    treeitr_dump_apply,
    treeitr_dump_exit,
};

static tree js_traverse_vec(VEC(tree, gc) *vec, treeitr_t *itr)
{
  int i;
  tree x, stmts = itr->init(NULL_TREE);
  if (VEC_length(tree, vec) > 0) {
      FOR_EACH_VEC_ELT(tree, vec, i, x) {
          stmts = itr->apply(x, stmts);
      }
  }
  return itr->exit(stmts);
}

static tree js_traverse_tree(tree stmts, treeitr_t *itr)
{
  tree x = itr->init(stmts);
  if (x) {
      if (IS_STMT_LIST(x)) {
          tree_stmt_iterator tsi;
          for (tsi = tsi_start(x); !tsi_end_p(tsi) ; tsi_next(&tsi)) {
              tree t = tsi_stmt(tsi);
              itr->apply(t, t);
          }
      } else {
          itr->apply(x, x);
      }
  }
  return itr->exit(stmts);
}

tree poplevel(int level, tree stmts)
{
  tree sub_stmts, block, bind;
  VEC(tree,gc) *sub_decls;
  if (stmts == NULL_TREE) {
      sub_stmts = get_level_stmts(level);
  } else {
      sub_stmts = stmts;
  }
  sub_decls = get_level_decls(level);
  block = js_make_block();
  if (sub_stmts) {
      tree decls = NULL_TREE;
      if (VEC_length(tree, sub_decls) > 0) {
          decls = js_tree_chain(sub_decls, decls);
      }
      BLOCK_VARS(block) = decls;
  } else {
      sub_stmts = js_build_empty_stmt();
      sub_decls = VEC_alloc(tree,gc,0);
  }
  bind = build3(BIND_EXPR, TREE_TYPE(block), BLOCK_VARS(block),
                BLOCK_EXPR_BODY(block), block);
  BIND_EXPR_BODY(bind) = sub_stmts;
  if (BIND_EXPR_BODY(bind) == NULL) {
      BIND_EXPR_BODY(bind) = js_build_empty_stmt();
  }
  TREE_SIDE_EFFECTS(bind) = true;
  clear_level(level);
  return sub_stmts;
}

static void js_append_to_function_decls(tree id, tree decl)
{
  js_symbol sym = new_jssymbol(id, decl);
  VEC_safe_push(js_symbol, gc, global_declsym, sym);
}

static void js_append_to_undefine_function_decls(tree id, tree decl)
{
  js_symbol sym = new_jssymbol(id, decl);
  VEC_safe_push(js_symbol, gc, global_undef_declsym, sym);
}

#include "object.c"
static tree js_build_static_string(tree cst)
{
  tree tmp;
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  gcc_assert (TREE_CODE(cst) == STRING_CST);
  tmp = build1(ADDR_EXPR, string_type_node, cst);
  VEC_safe_push(tree, gc, args, tmp);
  tmp = js_build_int(TREE_STRING_LENGTH(cst)+1);
  VEC_safe_push(tree, gc, args, tmp);
  tmp = static_string_new_node;
  return js_build_call_expr(tmp, args, false/*hasSelf*/, false);
}

static tree verify_and_convert_lhs(tree expr)
{
  debug0("lhs");
  if (TREE_CODE(expr) == IDENTIFIER_NODE) {
      return find_symbol(expr, push_to_symtable);
  }
  else if (TREE_CODE(expr) == MODIFY_EXPR) {
      error("TODO");
      //int3();
      return expr;
  }
  else if (TREE_CODE(expr) == STRING_CST) {
      return js_build_static_string(expr);
  }
  else if (TREE_CODE(expr) == CALL_EXPR) {
      return expr;
  }
  int3();
  return error_mark_node;
}

static tree verify_and_convert_rhs(tree expr)
{
  debug0("rhs");
  if (TREE_CODE(expr) == IDENTIFIER_NODE) {
      return find_symbol(expr, symbol_not_found);
  }
  else if (TREE_CODE(expr) == STRING_CST) {
      return js_build_static_string(expr);
  }

  return expr;
}

typedef tree (*ftreeconv) (JSOperator op, tree lhs, tree rhs);
static tree js_build_oplet_expr(JSOperator op, tree lhs, tree rhs)
{
  tree tmp;
  if (TREE_TYPE(rhs) == integer_type_node) {
      rhs = js_build_int_object(rhs);
  }
  //lhs = convert(TREE_TYPE(rhs), lhs);
  if (op != OpEQLET) {
      tmp = js_build_op2_expr(op, lhs, rhs);
  } else {
      tmp = rhs;
  }
  return build2(MODIFY_EXPR, JS_TREE_TYPE(lhs), lhs, tmp);
}

static tree convert_to_setter(tree expr, tree value)
{
  tree retval = error_mark_node;
  tree self, arg1;
  VEC(tree, gc) *args;
  gcc_assert(IS_GETTER(expr));

  self = CALL_EXPR_ARG(expr, 0);
  arg1 = CALL_EXPR_ARG(expr, 1);
  args = VEC_alloc(tree, gc, 0);
  VEC_safe_push(tree, gc, args, self);
  VEC_safe_push(tree, gc, args, copy_node(arg1));
  VEC_safe_push(tree, gc, args, value);
  retval = js_build_call_expr(setter_node, args, false, true);
  return retval;
}

static tree js_build_oplet_setter(JSOperator op, tree lhs, tree rhs)
{
  tree tmp;

  gcc_assert(IS_GETTER(lhs));
  if (op != OpEQLET) {
      tmp = js_build_op2_expr(op, lhs, rhs);
  } else {
      tmp = rhs;
  }

  return convert_to_setter(lhs, tmp);
}

tree js_build_let_expr(JSOperator op, tree lhs, tree rhs)
{
  tree retval = error_mark_node;
  debug0(__func__);
  TREE_USED(lhs) = true;
  TREE_USED(rhs) = true;
  lhs = verify_and_convert_lhs(lhs);
  rhs = verify_and_convert_rhs(rhs);
  if (TREE_CODE(lhs) == CALL_EXPR && !IS_GETTER(lhs) && !IS_SETTER(lhs)) {
      return lhs;
  }
  if (lhs != error_mark_node && rhs != error_mark_node) {
      ftreeconv f = js_build_oplet_expr;
      if (IS_GETTER(lhs)) {
          f = js_build_oplet_setter;
      }
      switch (op) {
        case OpEQLET :
          retval = f(OpEQLET, lhs, rhs);
          break;
      case OpADDLET  :
          retval = f(OpPlus, lhs, rhs);
          break;
      case OpSUBLET  :
          retval = f(OpMinus, lhs, rhs);
          break;
      case OpMULLET  :
          retval = f(OpMul, lhs, rhs);
          break;
      case OpDIVLET  :
          retval = f(OpDiv, lhs, rhs);
          break;
      case OpMODLET  :
          retval = f(OpMod, lhs, rhs);
          break;
      case OpLSFTLET :
          retval = f(OpLshift, lhs, rhs);
          break;
      case OpRSFTLET :
          retval = f(OpRshift, lhs, rhs);
          break;
      case OpSHFTLET :
          retval = f(OpShift, lhs, rhs);
          break;
      case OpANDLET  :
          retval = f(OpAnd, lhs, rhs);
          break;
      case OpXORLET  :
          retval = f(OpOr, lhs, rhs);
          break;
      case OpORLET   :
          retval = f(OpRshift, lhs, rhs);
          break;
        default :
          error("not surpport");
          exit(EXIT_FAILURE);
          break;
      }
  }
  return retval;
}

static tree js_convert_treetype(tree type)
{
  tree t;
  if (type == boolean_type_node) {
      t = js_build_int((int)JS_BOOL);
  }
  else if (type == integer_type_node) {
      t = js_build_int((int)JS_INT);
  }
  else if (type == double_type_node) {
      t = js_build_int((int)JS_FLOAT);
  }
  else if (type == obj_string_type_ptr) {
      t = js_build_int((int)JS_STRING);
  }
  else if (type == ptr_type_node) {
      t = js_build_int((int)JS_OBJECT);
  }
  else if (type == object_type_ptr) {
      t = js_build_int((int)JS_OBJECT);
  }
  else if (TREE_TYPE(type) == char_type_node) {
      t = js_build_int((int)JS_RAWSTR);
  }
  else if (type == intobj_type_ptr) {
      t = js_build_int((int)JS_INTOBJ);
  }
  else if (type == floatobj_type_ptr) {
      t = js_build_int((int)JS_FLTOBJ);
  } else {
      asm volatile("int3");
      fprintf(stderr, "no type match\n");
  }
  return t;
}
typedef void (*fvecsym)(tree *vec, tree t);
static void arg_append_with_type(tree *vec, tree t)
{
  vec[0] = js_convert_treetype(TREE_TYPE(t));
  vec[1] = t;
}
static void arg_append(tree *vec, tree t)
{
  vec[0] = t;
}

static void append_vec(tree *vec, int i, tree x, fvecsym f, bool typeCheck)
{
    gcc_assert(x);
    //if(TREE_CODE(x) == SAVE_EXPR) {
    //    asm volatile("int3");
    //    x = TREE_OPERAND(x, 0);
    //}
    if(TREE_CODE(x) == IDENTIFIER_NODE) {
        const char *name = IDENTIFIER_POINTER(x);
        //fprintf(stderr, "name=%s\n", name);
        x = find_symbol(x, symbol_not_found);
        if (TREE_TYPE(x) == obj_string_type_ptr) {
        }
        else if (TREE_CODE(x) == STRING_CST) {
            x = js_build_static_string(x);
        }
        if (typeCheck) {
            if (TREE_TYPE(x) == integer_type_node) {
                asm volatile("int3");
                fprintf(stderr, "id type check\n");
            }
            else if (TREE_TYPE(x) == double_type_node) {
                asm volatile("int3");
                fprintf(stderr, "id type check\n");
            }
        }
    }
    else {
        TREE_USED(x) = true;
        if (TREE_CODE(x) == ADDR_EXPR) {
        }
        else if (TREE_CODE(x) == CALL_EXPR) {
        }
        else if (TREE_CODE(x) == STRING_CST) {
            x = js_build_static_string(x);
        }
        if (typeCheck) {
            if (TREE_TYPE(x) == obj_string_type_ptr) {
            }
            else if (TREE_TYPE(x) == string_type_node) {
                int3();
                fprintf(stderr, "string\n");
            }
            else if (TREE_TYPE(x) == integer_type_node) {
                x = js_build_int_object(x);
            }
        }

    }
    f(vec+i, x);
}

static tree *init_args_builtin_print(int *n, VEC(tree,gc) *args)
{
  int argc = (*n) * 2 + 1;
  int idx = 0, idy = 1;
  tree x;
  tree *args_vec = XNEWVEC(tree, argc);
  args_vec[0] = js_build_int((*n));
  *n = argc;
  for(; VEC_iterate(tree,args,idx,x); ++idx) {
      append_vec(args_vec, idy, x, arg_append_with_type, true);
      idy+=2;
  }
  return args_vec;
}

static tree *init_args_default(int *n, VEC(tree,gc) *args ATTRIBUTE_UNUSED)
{
  int idx = 0;
  tree x;
  tree *args_vec = XNEWVEC(tree, *n);
  for(; VEC_iterate(tree,args,idx,x); ++idx) {
      append_vec(args_vec, idx, x, arg_append, true);
  }
  return args_vec;
}
static tree *init_args_builtin_notypecheck(int *n, VEC(tree,gc) *args ATTRIBUTE_UNUSED)
{
  int idx = 0;
  tree x;
  tree *args_vec = XNEWVEC(tree, *n);
  for(; VEC_iterate(tree,args,idx,x); ++idx) {
      append_vec(args_vec, idx, x, arg_append, false);
  }
  return args_vec;
}
static tree *js_init_args(const char *name, int *n, VEC(tree,gc) *args)
{
    if (strcmp(name, "print") == 0) {
        return init_args_builtin_print(n, args);
    }
    else if (strcmp(name, "__init__") == 0) {
        return init_args_builtin_notypecheck(n, args);
    }
    else if (strcmp(name, "static_new_string") == 0) {
        return init_args_builtin_notypecheck(n, args);
    }
    else if (strcmp(name, "int_new") == 0) {
        return init_args_builtin_notypecheck(n, args);
    }
    else if (strcmp(name, "float_new") == 0) {
        return init_args_builtin_notypecheck(n, args);
    }
    return init_args_default(n, args);
}

tree js_build_call_expr(tree expr, VEC(tree, gc) *args, bool hasSelf, bool box)
{
  tree retval = error_mark_node;
  int n;
  /*debug1(__func__);*/

  /* self.expr(args) */
  tree t = TREE_CHAIN(expr);
  if (t && TREE_CODE(t) == IDENTIFIER_NODE) {
      tree self = expr;
      expr = t;
      TREE_CHAIN(self) = NULL_TREE;
      VEC_safe_insert(tree, gc, args, 0, self);
  } else if (hasSelf) {
      if (TREE_CODE(expr) == IDENTIFIER_NODE) {
          /* self is not used */
          VEC_safe_insert(tree, gc, args, 0, null_pointer_node);
      } else if (TREE_TYPE(expr) == function_type_ptr) {
          if (TREE_CODE(expr) == CALL_EXPR) {
              tree fn = CALL_EXPR_FN(expr);
              tree id  = DECL_NAME(TREE_OPERAND(fn, 0));
              if (id == function_new_node) {
                  VEC_safe_insert(tree, gc, args, 0, null_pointer_node);
              } else if (id == getter_node) {
                  VEC_safe_insert(tree, gc, args, 0, save_expr(CALL_EXPR_ARG(expr, 0)));
              } else {
                  fprintf(stderr, "%s %d todo\n", __func__, __LINE__);
                  asm volatile("int3");
              }
          }
          n = VEC_length(tree, args);
          VEC_safe_insert(tree, gc, args, 0, expr);
          expr = function_call_node;
          VEC_safe_insert(tree, gc, args, 1, js_build_int_object(js_build_int(n)));
      } else {
          fprintf(stderr, "NO IDEA %s %dn", __func__, __LINE__);
          asm volatile("int3");
          fprintf(stderr, "???\n");
      }
  }
  n = VEC_length(tree, args);
  /* expr(args) */
  if (TREE_CODE(expr) == IDENTIFIER_NODE) {
      tree *args_vec;
      const char *name = IDENTIFIER_POINTER(expr);
      tree f = js_get_function_tree(name, args);
      tree restype = TREE_TYPE(TREE_TYPE(f));
      if (DECL_RESULT(f)) {
          tree resdecl = build_decl(CURRENT_LOCATION(),
                  RESULT_DECL, NULL_TREE, restype);
          DECL_CONTEXT(resdecl) = f;
          DECL_RESULT(f) = resdecl;
      }
      args_vec = js_init_args(name, &n, args);
      retval = build_call_expr_loc_array(CURRENT_LOCATION(), f, n, args_vec);

      if (box && restype == integer_type_node) {
          retval = js_build_int_object(retval);
      }
      else if (box && restype == double_type_node) {
          retval = js_build_float_object(retval);
      }
  }
  return retval;
}

tree js_build_typeof_expr(tree expr)
{
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  debug(__func__);
  TREE_USED(expr) = true;
  expr = verify_and_convert_rhs(expr);
  VEC_safe_push(tree, gc, args, expr);
  expr = js_build_id("typeof");
  return js_build_call_expr(expr, args, false/*hasSelf*/, false);
}
static tree js_build_int_op(enum tree_code code, tree lhs, tree rhs)
{
  tree expr = NULL_TREE;
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  switch(code) {
    case  PLUS_EXPR:
      expr = js_build_id("int_add");
      break;
    case  MINUS_EXPR:
      expr = js_build_id("int_sub");
      break;
    case  MULT_EXPR:
      expr = js_build_id("int_mul");
      break;
    case  EQ_EXPR:
      expr = js_build_id("int_eq");
      break;
    case  NE_EXPR:
      expr = js_build_id("int_neq");
      break;
    case  LT_EXPR:
      expr = js_build_id("int_lt");
      break;
    case  GT_EXPR:
      expr = js_build_id("int_gt");
      break;
    case FLOOR_MOD_EXPR:
      expr = js_build_id("int_mod");
      break;
    case FLOOR_DIV_EXPR:
      expr = js_build_id("int_div");
      break;
    default :
      error("not surpport");
      exit(EXIT_FAILURE);
      break;
  }
  VEC_safe_push(tree, gc, args, lhs);
  VEC_safe_push(tree, gc, args, rhs);
  return js_build_call_expr(expr, args, false, true);
}
static tree js_build_float_op(enum tree_code code, tree lhs, tree rhs)
{
  tree expr = NULL_TREE;
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  switch(code) {
    case  PLUS_EXPR:
      expr = js_build_id("float_add");
      break;
    case  MINUS_EXPR:
      expr = js_build_id("float_sub");
      break;
    case  MULT_EXPR:
      expr = js_build_id("float_mul");
      break;
    case  EQ_EXPR:
      expr = js_build_id("float_eq");
      break;
    case  NE_EXPR:
      expr = js_build_id("float_neq");
      break;
    case  LT_EXPR:
      expr = js_build_id("float_lt");
      break;
    case  GT_EXPR:
      expr = js_build_id("float_gt");
      break;
    case FLOOR_DIV_EXPR:
      expr = js_build_id("float_div");
      break;
    default :
      error("not surpport");
      exit(EXIT_FAILURE);
      break;
  }
  VEC_safe_push(tree, gc, args, lhs);
  VEC_safe_push(tree, gc, args, rhs);
  return js_build_call_expr(expr, args, false, true);
}

static tree js_build_string_op(enum tree_code code, tree lhs, tree rhs)
{
  tree expr = NULL_TREE;
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  switch(code) {
    case  PLUS_EXPR:
      expr = js_build_id("string_plus");
      break;
    case  EQ_EXPR:
      expr = js_build_id("string_eq");
      break;
    case  NE_EXPR:
      expr = js_build_id("string_neq");
      break;
    default :
      error("not surpport");
      exit(EXIT_FAILURE);
      break;
  }
  VEC_safe_push(tree, gc, args, convert(obj_string_type_ptr, lhs));
  VEC_safe_push(tree, gc, args, convert(obj_string_type_ptr, rhs));
  return js_build_call_expr(expr, args, false, true);
}
static tree js_build_operand(enum tree_code code, tree type, tree lhs, tree rhs)
{
  tree lhs_ty = TREE_TYPE(lhs);
  if (TREE_TYPE(lhs) == string_type_node) {
      lhs = js_build_static_string(lhs);
  }
  if (TREE_TYPE(lhs) == integer_type_node) {
      lhs = js_build_int_object(lhs);
  }
  if (TREE_TYPE(rhs) == integer_type_node) {
      rhs = js_build_int_object(rhs);
  }
  if (TREE_TYPE(lhs) == double_type_node) {
      lhs = js_build_float_object(lhs);
  }
  if (TREE_TYPE(rhs) == double_type_node) {
      rhs = js_build_float_object(rhs);
  }
  //lhs = convert(TREE_TYPE(rhs), lhs);
  if (lhs_ty == obj_string_type_ptr && TREE_TYPE(lhs) == object_type_ptr) {
      TREE_TYPE(lhs) = TREE_TYPE(rhs) = obj_string_type_ptr;
  }
  if (TREE_TYPE(lhs) == obj_string_type_ptr) {
      return js_build_string_op(code, lhs, rhs);
  }
  if (TREE_TYPE(lhs) == object_type_ptr) {
      tree lhs_saved = lhs;
      if (TREE_CODE(lhs_saved) == CALL_EXPR) {
          tree fn = CALL_EXPR_FN(lhs_saved);
          tree id  = DECL_NAME(TREE_OPERAND(fn, 0));
          //const char *fn_name = IDENTIFIER_POINTER(id);
          if (id == new_float_node) {
              return js_build_int_op(code, lhs, rhs);
          } else if (id == new_int_node) {
              return js_build_int_op(code, lhs, rhs);
          }
      }
      return js_build_int_op(code, lhs, rhs);
  }
  if (TREE_TYPE(lhs) == intobj_type_ptr) {
      return js_build_int_op(code, lhs, rhs);
  }
  if (TREE_TYPE(lhs) == floatobj_type_ptr) {
      return js_build_float_op(code, lhs, rhs);
  }
  return build2(code, type, lhs, rhs);
}

tree js_build_op2_expr(JSOperator op, tree lhs, tree rhs)
{
  tree retval = error_mark_node;
  debug1(__func__);
  TREE_USED(lhs) = true;
  TREE_USED(rhs) = true;
  lhs = save_expr(verify_and_convert_rhs(lhs));
  rhs = save_expr(verify_and_convert_rhs(rhs));
  if (lhs != error_mark_node && rhs != error_mark_node) 
    {
      enum tree_code code;
      /* node type is float or integer, string */
      tree type = TREE_TYPE(rhs);
      switch(op) {
        case  OpPlus:
          code = /* a + b */PLUS_EXPR;
          break;
        case  OpMinus:
          code = /* a - b */MINUS_EXPR;
          break;
        case  OpMul:
          code = /* a * b */MULT_EXPR;
          break;
        case  OpDiv:
          code = /* a / b */FLOOR_DIV_EXPR;
          break;
        case  OpMod:
          code = /* a % b */FLOOR_MOD_EXPR;
          break;
        case  OpLshift:
          code = /* a << b */LSHIFT_EXPR;
          break;
        case  OpRshift:
          code = /* a >> b */RSHIFT_EXPR;
          break;
        case  OpOr:
          code = /* a | b */ BIT_IOR_EXPR;
          break;
        case  OpXor:
          code = /* a ^ b */ BIT_XOR_EXPR;
          break;
        case  OpAnd:
          code = /* a & b */ BIT_AND_EXPR;
          break;
        case  OpLT:
          type = boolean_type_node;
          code = /* a <  b */LT_EXPR;
          break;
        case  OpLE:
          type = boolean_type_node;
          code = /* a <= b */LE_EXPR;
          break;
        case  OpGT:
          type = boolean_type_node;
          code = /* a >  b */GT_EXPR;
          break;
        case  OpGE:
          type = boolean_type_node;
          code = /* a >= b */GE_EXPR;
          break;
        case  OpEQ:
          type = boolean_type_node;
          code = /* a == b */EQ_EXPR;
          break;
        case  OpNE:
          type = boolean_type_node;
          code = /* a != b */NE_EXPR;
          break;;
        case  OpSTREQ:
          return js_build_string_op(EQ_EXPR, lhs, rhs);
          break;
        case  OpSTRNE:
          return js_build_string_op(NE_EXPR, lhs, rhs);
          break;
        default :
          error("not surpport");
          exit(EXIT_FAILURE);
          break;
      }
      retval = js_build_operand(code, type, lhs, rhs);
    }
  return retval;
}

tree js_build_op1_expr(JSOperator op, tree r)
{
  tree t, retval = error_mark_node;
  debug(__func__);
  TREE_USED(r) = true;
  t = save_expr(verify_and_convert_rhs(r));
  TREE_USED(t) = 1;
  if (t != error_mark_node)
    {
      enum tree_code code;
      switch(op) {
        case  OpNot:
          code = /* !a */BIT_NOT_EXPR;
          break;
        case  OpPRED_INC:
          code = /* ++i */PREINCREMENT_EXPR;
          return js_build_let_expr(OpADDLET, r, js_build_int(1));
        case  OpPRED_DEC:
          code = /* --i */PREDECREMENT_EXPR;
          return js_build_let_expr(OpSUBLET, r, js_build_int(1));
        case  OpPOST_INC:
          code = /* i-- */POSTINCREMENT_EXPR;
          return js_build_let_expr(OpADDLET, r, js_build_int(1));
        case  OpPOST_DEC:
          code = /* i-- */POSTDECREMENT_EXPR;
          return js_build_let_expr(OpSUBLET, r, js_build_int(1));
        default :
          error("not surpport");
          break;
      }
      retval = build1(code, JS_TREE_TYPE(t), t);
      TREE_SIDE_EFFECTS(retval) = true;
    }
  return retval;
}

static void remove_self(int level, tree stmt)
{
  tree body = get_level_stmts(level);
  clear_level_stmts(level);
  if (TREE_CODE(body) == STATEMENT_LIST) {
      tree_stmt_iterator tsi;
      for (tsi = tsi_start(body); !tsi_end_p(tsi) ; tsi_next(&tsi)) {
          tree t = tsi_stmt(tsi);
          if (t != stmt) {
              js_append_to_current_stmtlist(t, level);
          }
      }
  }
}

static tree cut_from_level_stmts(tree stmt)
{
  int level = js__block_level + 2;
  remove_self(js__block_level, stmt);
  clear_level_stmts(level);
  clear_level_decls(level);
  js_append_to_current_stmtlist(stmt, level);
  return poplevel(level, stmt);
}

tree js_build_if_expr(tree cond, tree stmt1, tree stmt2)
{
  /* if stmt2 != NULL_TREE, source code is "if (cond) stmt1 else stmt2,",
   * other case, source code is "if (cond) stmt1;
   */
  tree retval = error_mark_node;
  tree saved = save_expr(cond);
  TREE_USED(saved) = true;

  if (stmt1 && !IS_STMT_LIST(stmt1)) {
      stmt1 = cut_from_level_stmts(stmt1);
  }
  if (stmt2 && !IS_STMT_LIST(stmt2)) {
      stmt2 = cut_from_level_stmts(stmt2);
  }
  retval = build3(COND_EXPR, void_type_node, cond, stmt1, stmt2);
  return retval;
}

static void swap_bool_tree_code(tree t)
{
  switch (TREE_CODE(t)) {
    case LT_EXPR:
      TREE_SET_CODE(t,GE_EXPR);
      break;
    case LE_EXPR:
      TREE_SET_CODE(t,GT_EXPR);
      break;
    case  GT_EXPR:
      TREE_SET_CODE(t,LE_EXPR);
      break;
    case  GE_EXPR:
      TREE_SET_CODE(t,LT_EXPR);
      break;
    case  OpEQ:
      TREE_SET_CODE(t,NE_EXPR);
      break;
    case  OpNE:
      TREE_SET_CODE(t,EQ_EXPR);
      break;
    default:
      break;
      //error("TODO");
  }
}

tree js_build_while_expr(tree cond, tree body, bool isDoWhile ATTRIBUTE_UNUSED)
{
  tree retval = error_mark_node;
  tree exit;
  debug(__func__);
  if (TREE_TYPE(cond) == boolean_type_node) {
      swap_bool_tree_code(cond);
  }
  if (body && !IS_STMT_LIST(body)) {
      body = cut_from_level_stmts(body);
  }
  exit = build1(EXIT_EXPR, void_type_node, cond);
  append_to_statement_list(exit, &body);
  retval = build1(LOOP_EXPR, void_type_node, body);
  return retval;
}

tree js_build_for_expr(tree init, tree cond, tree inc, tree body)
{
  tree retval = alloc_stmt_list();
  debug(__func__);
  if (TREE_TYPE(cond) == boolean_type_node) {
      swap_bool_tree_code(cond);
  }
  if (body && !IS_STMT_LIST(body)) {
#if 0
      if (TREE_CODE(body) != NOP_EXPR) {
          body = cut_from_level_stmts(body);
      } else {
          body = alloc_stmt_list();
      }
#endif
      if (TREE_CODE(body) == NOP_EXPR) {
          body = alloc_stmt_list();
      }
  }
  append_to_statement_list(inc, &body);
  append_to_statement_list(build1(EXIT_EXPR, void_type_node, cond), &body);

  append_to_statement_list(init, &retval);
  append_to_statement_list(build1(LOOP_EXPR, void_type_node, body), &retval);
  return retval;

}

tree js_get_default_value(tree type)
{
  if (type == boolean_type_node) {
      return boolean_false_node;
  } else if (type == integer_type_node) {
      return js_build_int(0);
  } else if (type == double_type_node) {
      error("TODO");
  }
  return js_build_int(0);
  /*return null_pointer_node;*/
}

static tree chain_args(int level)
{
  int i;
  tree decls = NULL_TREE;
  for (i = level; i >= 0; --i) {
      js_symbol x;
      VEC(js_symbol,gc) *args = get_level_args(i);
      if (args) {
          int j;
          FOR_EACH_VEC_ELT_REVERSE(js_symbol, args, j, x) {
              tree d = x->decl;
              TREE_CHAIN(d) = decls;
              decls = d;
          }
          break;
      }
  }
  return decls;
}
static VEC(tree,gc) *chain_args_type(int level)
{
  int i;
  VEC(tree,gc) *retval = VEC_alloc(tree, gc, 0);
  for (i = level; i >= 0; --i) {
      js_symbol x;
      VEC(js_symbol,gc) *args = get_level_args(i);
      if (args) {
          int j;
          FOR_EACH_VEC_ELT_REVERSE(js_symbol, args, j, x) {
              VEC_safe_push(tree, gc, retval, JS_TREE_TYPE(x->decl));
          }
          break;
      }
  }
  return retval;
}

tree js_build_function_type(tree rettype, VEC(tree,gc) *vec)
{
  int i;
  tree params = NULL_TREE, x = NULL_TREE;
  for (i = 0; VEC_iterate(tree,vec, i, x); ++i) {
      chainon(params, tree_cons(NULL_TREE, x, NULL_TREE) );
  }
  return build_function_type(rettype, params);
}

static VEC(tree,gc) *js_build_typearray(VEC(tree,gc) *args)
{
  int i;
  tree x = NULL_TREE;
  VEC(tree, gc) *params = VEC_alloc(tree, gc, 0);
  for (i = 0; VEC_iterate(tree, args, i, x); ++i) {
      tree type = TREE_TYPE(x);
      if (type == integer_type_node) {
          type = intobj_type_ptr; 
      }
      VEC_safe_push(tree, gc, params, type);
  }
  return params;
}

static tree js_get_function_tree_from(VEC(js_symbol, gc) *decls, const char *name,
                                      VEC(tree, gc) *args, bool undef)
{
  int i;
  js_symbol_t *itx = NULL;

  FOR_EACH_VEC_ELT(js_symbol, decls, i, itx) {
      if (IS_FUNCTION_DECL(itx->decl)) {
          const char *fn_name = IDENTIFIER_POINTER(itx->name);
          if (strcmp(name, fn_name) == 0) {
              return itx->decl;
          }
      }
  }

  if (undef) {
      tree id = js_build_id(name);
      VEC(tree,gc) *params = js_build_typearray(args);
      tree fn_type  = js_build_function_type(object_type_ptr, params);
      tree fn_decl  = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL, id, fn_type);
      js_append_to_undefine_function_decls(id, fn_decl);
      warning_at(CURRENT_LOCATION(), 0, "undefined function or variable : %s", name);
      return fn_decl;
  }
  return NULL_TREE;
}

static tree js_get_function_tree(const char *name, VEC(tree,gc) *args)
{
  tree f = js_get_builtin_function_decl(name);
  if (f == NULL_TREE) {
      f = js_get_function_tree_from(global_declsym, name, args, false);
      if (f == NULL_TREE) {
          f = js_get_function_tree_from(global_undef_declsym, name, args, true);
      }
  }
  return f;
}

static tree create_entrypoint(const char *filename)
{
  extern FILE *js_in;
  tree fn_type, fn_decl;
  tree main_block;
  tree main_stmts;
  VEC(tree,gc) *args_list = VEC_alloc(tree, gc, 0);
  VEC(tree,gc) *main_block_decls;

  debug("main");
  js_in = fopen(filename, "r");
  if (js_in == NULL) {
      error("Cannot open input file %s", filename);
      exit(EXIT_FAILURE);
  }

  fn_type = js_build_function_type(void_type_node, NULL);
  fn_decl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
                       get_identifier("__entrypoint__"), fn_type);

  DECL_CONTEXT(fn_decl) = NULL_TREE;
  TREE_STATIC(fn_decl) = true;
  TREE_PUBLIC(fn_decl) = true;

  DECL_ARGUMENTS(fn_decl) = NULL_TREE;
  /* Define the retutrn type (represented by RESULT_DECL) 
   * for the main function */
  tree main_ret = build_decl(BUILTINS_LOCATION, RESULT_DECL,
                        NULL_TREE, TREE_TYPE(fn_type));
  DECL_CONTEXT(main_ret) = fn_decl;
  DECL_ARTIFICIAL(main_ret) = true;
  DECL_IGNORED_P(main_ret)  = true;
  DECL_RESULT(fn_decl) = main_ret;

  main_block = build_block(NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
  DECL_INITIAL(fn_decl) = main_block;

  /* Call the bison parser */
#if 0
  extern int js_debug;
  js_debug = 1;
#endif
  /*debug("parse start");*/
  js_parse();
  /*debug("parse end");*/

  main_stmts = get_level_stmts(0);
#if 0
  js_traverse_tree(main_stmts, &treeitr_dump_tree);
#endif

  main_block_decls = get_level_decls(0);
  js_build_function_set_decl_stmt(fn_decl, main_block_decls, main_stmts);
  {
    FILE *tu_stream = dump_begin (TDI_tu, NULL);
    if (tu_stream) {
        dump_node(fn_decl, 0, tu_stream);
        dump_end(TDI_tu, tu_stream);
    }
  }
  /* Prepare the function for the GCC middle-end */
  gimplify_function_tree(fn_decl);
  cgraph_add_new_function(fn_decl, false);
  cgraph_finalize_function(fn_decl, true);
  VEC_safe_push(tree, gc, global_decls, fn_decl );

  fclose(js_in);
  return fn_decl;
}

void js_parse_file(const char *filename)
{
  extern FILE *js_in;
  tree fn_type, fn_decl;
  tree main_ret, main_block;
  tree main_ret_expr, main_stmts, main_set_ret;
  tree main_init;
  tree args = NULL_TREE;
  VEC(tree,gc) *args_list = VEC_alloc(tree, gc, 0);
  VEC(tree,gc) *main_block_decls;

  VEC_safe_push(tree, gc, args_list, integer_type_node);
  VEC_safe_push(tree, gc, args_list, ptr_type_node);
  fn_type = js_build_function_type(integer_type_node, args_list);
  fn_decl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
                       get_identifier("main"), fn_type);

  DECL_CONTEXT(fn_decl) = NULL_TREE;
  TREE_STATIC(fn_decl) = true;
  TREE_PUBLIC(fn_decl) = true;

    {
      tree type, arg1, arg2;
      VEC(tree,gc) *args_list = VEC_alloc(tree, gc, 0);
      type = integer_type_node;
      arg1 = js_build_id("argc");
      arg1 = build_decl(BUILTINS_LOCATION, PARM_DECL, arg1, type);
      DECL_ARG_TYPE(arg1) = type;
      type = ptr_type_node;
      arg2 = js_build_id("argv");
      arg2 = build_decl(BUILTINS_LOCATION, PARM_DECL, arg2, type);
      DECL_ARG_TYPE(arg2) = type;
      VEC_safe_push(tree, gc, args_list, arg1);
      VEC_safe_push(tree, gc, args_list, arg2);
      main_init = js_build_call_expr(js_init_node, args_list, false, false);
      args = arg1;
      TREE_CHAIN(arg1) = arg2;
    }
  DECL_ARGUMENTS(fn_decl) = args;
  /* Define the retutrn type (represented by RESULT_DECL) 
   * for the main function */
  main_ret = build_decl(BUILTINS_LOCATION, RESULT_DECL,
                        NULL_TREE, TREE_TYPE(fn_type));
  DECL_CONTEXT(main_ret) = fn_decl;
  DECL_ARTIFICIAL(main_ret) = true;
  DECL_IGNORED_P(main_ret)  = true;

  DECL_RESULT(fn_decl) = main_ret;

  main_block = build_block(NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
  DECL_INITIAL(fn_decl) = main_block;

  tree t = alloc_stmt_list();
  append_to_statement_list(main_init, &t);

  {
      tree fn_entry = create_entrypoint(filename);
      tree tmp = build1(ADDR_EXPR, build_pointer_type(void_type_node), fn_entry);
      VEC(tree, gc) *args_list = VEC_alloc(tree, gc, 0);
      VEC_safe_push(tree, gc, args_list, tmp);
      tree call_entry = js_build_call_expr(call_entry_node, args_list, false, false);
      append_to_statement_list(call_entry, &t);
  }
  main_stmts = t;

  main_set_ret = build2(MODIFY_EXPR, TREE_TYPE(main_ret),
                        main_ret, js_build_int(0));
  TREE_USED(main_set_ret) = true;
  main_ret_expr = build1(RETURN_EXPR, void_type_node, main_set_ret);
  append_to_statement_list(main_ret_expr, &main_stmts);

  main_block_decls = get_level_decls(0);
  js_build_function_set_decl_stmt(fn_decl, main_block_decls, main_stmts);
  {
    FILE *tu_stream = dump_begin (TDI_tu, NULL);
    if (tu_stream) {
        dump_node(fn_decl, 0, tu_stream);
        dump_end(TDI_tu, tu_stream);
    }
  }
  /* Prepare the function for the GCC middle-end */
  gimplify_function_tree(fn_decl);
  cgraph_add_new_function(fn_decl, false);
  cgraph_finalize_function(fn_decl, true);
  VEC_safe_push(tree, gc, global_decls, fn_decl );
}

typedef void (*fret)(tree stmt, tree *rettype_p);
static void check_return(tree stmt ATTRIBUTE_UNUSED,
                         tree *rettype_p ATTRIBUTE_UNUSED)
{
  tree tmp = TREE_OPERAND(stmt, 0);
  *rettype_p = TREE_TYPE(tmp);
}


static bool function_has_return(tree body, tree *rettype_p, fret f)
{
  bool retval = false;
  tree_stmt_iterator tsi;
  for (tsi = tsi_start(body); !tsi_end_p(tsi) ; tsi_next(&tsi)) {
      tree t = tsi_stmt(tsi);
      if (TREE_CODE(t) == STATEMENT_LIST) {
          retval = retval || function_has_return(t, rettype_p, f);
      }
      if (TREE_CODE(t) == RETURN_EXPR) {
          f(t, rettype_p);
          retval = true;
      }
  }
  return retval;
}

tree js_build_return_stmt(tree expr)
{
  tree rettype = void_type_node;
  tree setret;
  if (expr == NULL) {
      error("TODO");
      exit(EXIT_FAILURE);
  }
  expr = verify_and_convert_rhs(expr);
  if (expr) {
      rettype = TREE_TYPE(expr);
  }
  /* replace "setret" node later (see js_build_function_return) */
  setret = build2(MODIFY_EXPR, rettype, NULL_TREE, expr);
  return build1(RETURN_EXPR, void_type_node, setret);
}

static tree js_get_function_decl(tree id, tree fn_type)
{
  int i;
  js_symbol_t *x = NULL;
  FOR_EACH_VEC_ELT(js_symbol, global_undef_declsym, i, x) {
      if (IS_FUNCTION_DECL(x->decl)) {
          const char *fn_name  = IDENTIFIER_POINTER(id);
          const char *sym_name = IDENTIFIER_POINTER(x->name);
          if (strcmp(fn_name, sym_name+2) == 0 && x->decl != NULL_TREE) {
              tree fn_decl = x->decl;
              x->decl = NULL_TREE;
              TREE_TYPE(fn_decl) = fn_type;
              DECL_SOURCE_LOCATION(fn_decl) = CURRENT_LOCATION();
              return fn_decl;
          }
      }
  }
  return build_decl(CURRENT_LOCATION(), FUNCTION_DECL, id, fn_type);
}

static void js_build_function_set_decl_stmt(tree fn_decl, VEC(tree,gc) *decls, tree stmts)
{
  tree declare_vars = NULL_TREE, bind = NULL_TREE;
  if (VEC_length(tree, decls) > 0) {
      declare_vars = js_tree_chain(decls, declare_vars);
  }
  if (declare_vars != NULL_TREE) {
      tree bl = make_node(BLOCK);
      BLOCK_SUPERCONTEXT(bl) = fn_decl;
      DECL_INITIAL(fn_decl) = bl;
      BLOCK_VARS(bl) = declare_vars;
      TREE_USED(bl) = 1;
      bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(bl), NULL_TREE, bl);
      TREE_SIDE_EFFECTS(bind) = 1;

      BIND_EXPR_BODY(bind) = stmts;
      stmts = bind;
  }
  DECL_SAVED_TREE(fn_decl) = stmts;
}

static tree js_build_function_return(tree *rettype_p, tree body, tree *retdecl_p)
{
  tree_stmt_iterator tsi;
  tree stmts = NULL_TREE, retdecl = *retdecl_p;
  bool hasReturnStmt = function_has_return(body, rettype_p, check_return);
  tree rettype = *rettype_p;
  VEC(tree, gc) *vec = VEC_alloc(tree, gc, 0);
  gcc_assert(IS_STMT_LIST(body));
  if (!hasReturnStmt) {
      rettype = void_type_node;
  }
  retdecl = build_decl(CURRENT_LOCATION(), RESULT_DECL,
                       NULL_TREE, rettype);
  for (tsi = tsi_start(body); !tsi_end_p(tsi) ; tsi_next(&tsi)) {
      tree t = tsi_stmt(tsi);
      if (TREE_CODE(t) == RETURN_EXPR) {
          tree expr = retdecl;
          if (rettype != void_type_node) {
              tree tmp;
              expr = TREE_OPERAND(t ,0);
              tmp = TREE_OPERAND(expr, 1);
              TREE_OPERAND(expr, 1) = NULL_TREE;
              expr = build2(MODIFY_EXPR, rettype, retdecl, tmp);
          }
          t = build1(RETURN_EXPR, void_type_node, expr);
      }
      VEC_safe_push(tree, gc, vec, t);
  }
  if (!hasReturnStmt) {
      tree t = build1(RETURN_EXPR, void_type_node, NULL);
      VEC_safe_push(tree, gc, vec, t);
  }
  stmts = js_traverse_vec(vec, &treeitr_build_stmtlists);
  *retdecl_p = retdecl;
  *rettype_p = rettype;
  return stmts;
}

tree js_build_function_decl(tree id, tree stmts, bool declObject)
{
  VEC(tree,gc) *block_decls;
  tree block_stmts, retdecl, name;
  tree fn_type, fn_decl, fn_block;
  tree rettype = integer_type_node;
  int level = check_level_with_expr(stmts);
  VEC(tree,gc) *params = chain_args_type(level);
  name = id;
  /*
    {
      const char *str = IDENTIFIER_POINTER(id);
      char buf[128];
      sprintf(buf, "__%s", str);
      name = js_build_id(buf);
    }
  */

  block_stmts = stmts;
  if (!block_stmts)
    block_stmts = alloc_stmt_list();
  block_stmts = js_build_function_return(&rettype, block_stmts, &retdecl);

  fn_type  = js_build_function_type(rettype, params);
  fn_decl  = js_get_function_decl(name, fn_type);
  fn_block = build_block(NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
  DECL_CONTEXT(fn_decl) = NULL_TREE;
  TREE_STATIC(fn_decl)  = true;
  TREE_NOTHROW(fn_decl) = true;
  TREE_PUBLIC(fn_decl)  = true;
  DECL_ARGUMENTS(fn_decl) = chain_args(js__block_level);
  DECL_INITIAL(fn_decl) = fn_block;
  if (retdecl) {
      DECL_IGNORED_P(retdecl) = true;
      DECL_CONTEXT(retdecl) = fn_decl;
      DECL_RESULT(fn_decl) = retdecl;
  }

  block_decls = get_level_decls(level+1);
  js_build_function_set_decl_stmt(fn_decl, block_decls, block_stmts);

  gimplify_function_tree(fn_decl);
  cgraph_finalize_function(fn_decl, false);
  VEC_safe_push(tree, gc, global_decls, fn_decl);
  js_append_to_function_decls(name, fn_decl);
  clear_level_args(js__block_level);
  //clear_level(level);

  if (declObject) {
      tree tmp, decl;
      VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
      const char *str = IDENTIFIER_POINTER(id);
      char buf[128];
      sprintf(buf, "__%s", str);
      //fprintf(stderr, "id=%s %d %d\n", str, js__block_level, level);
      id = js_build_id(buf);

      decl = build_decl(CURRENT_LOCATION(), VAR_DECL, id, object_type_ptr);
      TREE_STATIC(decl) = true;
      TREE_PUBLIC(decl) = true;
      push_decl_to_symtable(id, decl);

      /* create function object */
      tmp = build1(ADDR_EXPR, build_pointer_type(void_type_node), fn_decl);
      VEC_safe_push(tree, gc, args, tmp);
      tmp = js_build_call_expr(function_new_node, args, false/*hasSelf*/, false);
      tmp = build2(MODIFY_EXPR, JS_TREE_TYPE(tmp), decl, tmp);
      write_global_script(tmp);
  }

  return fn_decl;
}

tree js_build_function_object(tree name, tree body)
{
  static int tmpvalue = 0;
  tree tmp, fn_decl;
  VEC(tree, gc) *args;
  if (name == NULL_TREE) {
      char buf[20] = {};
      sprintf(buf, "tmp%d", tmpvalue++);
      name = js_build_id(buf);
  }
  fn_decl = js_build_function_decl(name, body, false);
  args = VEC_alloc(tree, gc, 0);
  tmp = build1(ADDR_EXPR, build_pointer_type(void_type_node), fn_decl);
  VEC_safe_push(tree, gc, args, tmp);
  tmp = function_new_node;
  return js_build_call_expr(tmp, args, false/*hasSelf*/, false);
}

tree js_debug_tree(tree expr ATTRIBUTE_UNUSED)
{
  tree type ATTRIBUTE_UNUSED = TREE_TYPE(expr);
  enum tree_code code = TREE_CODE(expr);
  const char *name = "";
  if (code == IDENTIFIER_NODE) {
      name = IDENTIFIER_POINTER(expr);
  }
  int3();
  return expr;
}

static int string_eq (const char *text1, const char *text2)
{
    return strcmp(text1, text2) == 0;
}

static int id_tree_text_eq (const tree lhs, const char *text)
{
    return string_eq(IDENTIFIER_POINTER(lhs), text);
}

static int id_tree_eq (const tree lhs, const tree rhs)
{
    return id_tree_text_eq(lhs, IDENTIFIER_POINTER(rhs));
}

tree js_build_propaty_expr(tree self, tree prop)
{
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  tree expr = getter_node;
  const char *name = "";
  if (TREE_CODE(self) == IDENTIFIER_NODE) {
      const char *math = IDENTIFIER_POINTER(self);
      name = IDENTIFIER_POINTER(prop);
      if (strcmp("Math", math) == 0) {
          if (string_eq(name, "abs"))
              expr = js_build_id("Math_abs");
          else if (string_eq(name, "acos"))
              expr = js_build_id("Math_acos");
          else if (string_eq(name, "asin"))
              expr = js_build_id("Math_asin");
          else if (string_eq(name, "atan"))
              expr = js_build_id("Math_atan");
          else if (string_eq(name, "atan2"))
              expr = js_build_id("Math_atan2");
          else if (string_eq(name, "ceil"))
              expr = js_build_id("Math_ceil");
          else if (string_eq(name, "cos"))
              expr = js_build_id("Math_cos");
          else if (string_eq(name, "exp"))
              expr = js_build_id("Math_exp");
          else if (string_eq(name, "floor"))
              expr = js_build_id("Math_floor");
          else if (string_eq(name, "log"))
              expr = js_build_id("Math_log");
          else if (string_eq(name, "max"))
              expr = js_build_id("Math_max");
          else if (string_eq(name, "min"))
              expr = js_build_id("Math_min");
          else if (string_eq(name, "pow"))
              expr = js_build_id("Math_pow");
          else if (string_eq(name, "random"))
              expr = js_build_id("Math_random");
          else if (string_eq(name, "round"))
              expr = js_build_id("Math_round");
          else if (string_eq(name, "sin"))
              expr = js_build_id("Math_sin");
          else if (string_eq(name, "sqrt"))
              expr = js_build_id("Math_sqrt");
          else if (string_eq(name, "tan"))
              expr = js_build_id("Math_tan");
          else {}
          if (expr != getter_node) {
              return expr;
              //return js_build_call_expr(expr, args, false);
          }
      }
  }
  if (TREE_CODE(prop) == IDENTIFIER_NODE) {
      name = IDENTIFIER_POINTER(prop);
      prop = js_build_static_string(js_build_string(name));
  }
  VEC_safe_push(tree, gc, args, self);
  /* string.length */
  if (strcmp(name, "prototype") == 0) {
      expr = get_prototype_node;
  }

  if (TREE_TYPE(self) == obj_string_type_ptr && strcmp(name, "length") == 0) {
      expr = string_length_node;
  } else {
      VEC_safe_push(tree, gc, args, prop);
  }

  return js_build_call_expr(expr, args, false, false);
}

tree js_build_new_expr(tree expr, VEC(tree, gc) *args)
{
  tree retval = error_mark_node;
  tree self = error_mark_node;
  if (TREE_CODE(expr) == IDENTIFIER_NODE) {
      VEC(tree, gc) *args_ = VEC_alloc(tree, gc, 0);
      const char *name = IDENTIFIER_POINTER(expr);
      //fprintf(stderr, "%s %d\n", name, args_);
      //char buf[128];
      //const char *name = IDENTIFIER_POINTER(expr);
      //sprintf(buf, "__%s", name);
      //expr = js_build_id(buf);
      /*
      if (strcmp(name, "object") == 0) {
      }
      else if (strcmp(name, "string") == 0) {
      }
      else {
      }
      */
      VEC_safe_push(tree, gc, args_, js_build_int(JS_OBJECT));
      self = js_build_call_expr(new_node, args_, false, true);
      VEC_free(tree, gc, args_);
  }

  VEC_safe_insert(tree, gc, args, 0, self);
  retval = js_build_call_expr(expr, args, false, true);
  return retval;
}

tree js_build_throw_expr(tree expr ATTRIBUTE_UNUSED)
{
  VEC(tree, gc) *args = VEC_alloc(tree, gc, 0);
  return js_build_call_expr(abort_node, args, false/*hasSelf*/, false);
}

tree write_global_script(tree stmts)
{
    debug(__func__);
    /*
    debug("debug start");
    js_traverse_tree(stmts, &treeitr_dump_tree);
    debug("debug end");
    */
    js_append_to_current_stmtlist(stmts, 0);
    return NULL_TREE;
}
tree js_build_array_literal(VEC(tree, gc) *elem)
{
    int len = VEC_length(tree, elem);
    VEC_safe_insert(tree, gc, elem, 0, js_build_int(len));
    return js_build_call_expr(array_new_node, elem, false, false);
}
tree js_build_array_expr(tree ary, tree idx);
#define CLASS_PROTOCOL_LIST(CLASS) TREE_VEC_ELT (TYPE_LANG_SLOT_1 (CLASS), 4)

static tree lookup_field(tree type, tree name)
{
    tree field;
    for (field = TYPE_FIELDS(type); field; field = DECL_CHAIN(field)) {
        if (DECL_NAME(field) == name) {
            return field;
        }
    }
    return NULL_TREE;
}
static tree build_address_of(tree v)
{
    return build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(v)), v);
}

tree js_build_array_expr(tree ref, tree index)
{
    tree lhs = verify_and_convert_lhs(ref);
    tree field = lookup_field(array_type_node, get_identifier("__list"));
    tree size_exp = fold_convert(sizetype, size_in_bytes(integer_type_node));
    tree idx   = build2(MULT_EXPR, sizetype, fold_convert(sizetype, index), size_exp);

    if (field) {
        /* TODO : wrong access */
        tree list  = build3(COMPONENT_REF, TREE_TYPE(field), lhs, field, NULL_TREE);
        tree node  = build1(NOP_EXPR, integer_type_ptr, build_address_of(list));

        node = build2(POINTER_PLUS_EXPR, TREE_TYPE(node), node, idx);
        node = build1(INDIRECT_REF, TREE_TYPE(TREE_TYPE(node)), node);
        return node;
    }
    return NULL_TREE;
}


